package com.sparrow.common.util;

import com.sparrow.common.event.IEventInvoker;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.bytecode.assign.Assigner;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import static net.bytebuddy.matcher.ElementMatchers.named;

/**
 * ByteBuddy是一个代码生成和操作库，用于在 Java 应用程序运行时创建和修改 Java 类
 */
public class ByteBuddyUtil {


    public static IEventInvoker generateInvoker(Class<?> targetClass, Method targetMethod, Object targetBean) throws Exception {
        int paramCount = targetMethod.getParameterCount();

        // 1. 创建动态类型但不加载
        //创建一个 ByteBuddy 实例，动态生成一个类，这个类继承自 PacketInvoker
        DynamicType.Unloaded<? extends IEventInvoker> dynamicTypeUnloaded = new ByteBuddy()
                .subclass(IEventInvoker.class)
                //给动态生成的类命名，格式是：目标类全名 + $ + 目标方法名 + $Invoker
                .name(targetClass.getName() + "$" + targetMethod.getName() + "$Invoker")
                //给这个类定义两个私有且不可变（final）的字段 target 存放目标类实例对象。method 存放目标方法的 Method 对象
                .defineField("target", targetClass, Modifier.PRIVATE | Modifier.FINAL)
                .defineField("method", Method.class, Modifier.PRIVATE | Modifier.FINAL)
                //定义一个公有构造函数，参数类型为目标类和 Method 对象。
                .defineConstructor(Visibility.PUBLIC)
                .withParameters(targetClass, Method.class)
                //构造函数实现逻辑：1先调用父类 Object 的无参构造器。2然后把构造函数的第一个参数（目标对象）赋值给字段 target。3把构造函数的第二个参数（方法对象）赋值给字段 method。
                .intercept(MethodCall.invoke(Object.class.getConstructor())
                        .andThen(FieldAccessor.ofField("target").setsArgumentAt(0))
                        .andThen(FieldAccessor.ofField("method").setsArgumentAt(1)))
                //拦截重写 invoke 方法（假设 PacketInvoker 定义了 invoke 方法）。
                .method(named("invoke"))
                .intercept(MethodCall.invoke(targetMethod)
                        //调用目标方法 targetMethod
                        .onField("target")
                        //参数从 invoke 方法的第一个参数数组中取，长度为 paramCount
                        .withArgumentArrayElements(0, paramCount)
                        //使用动态类型转换（Typing.DYNAMIC），保证传参时类型兼容
                        .withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC))
                .method(named("getMethod"))
                .intercept(FieldAccessor.ofField("method"))
                .make();

        // 2. 保存到本地目录，目录不存在会自动创建
        if (PropertiesUtil.getPropertyOrDefault("byte_buddy_out", "false").equals("true")) {
            String path = PropertiesUtil.getProperty("byte_buddy_out_path");
            File saveDir = new File(path);
            dynamicTypeUnloaded.saveIn(saveDir);
        }

        // 3. 加载动态类
        Class<? extends IEventInvoker> dynamicType = dynamicTypeUnloaded
                .load(ByteBuddyUtil.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                .getLoaded();

        // 4. 返回实例
        return dynamicType.getConstructor(targetClass, Method.class)
                .newInstance(targetBean, targetMethod);
    }
}
